package com.wedu.modules.dss.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.wedu.common.utils.PageUtils;
import com.wedu.common.utils.Query;
import com.wedu.modules.dss.dao.DeviceDao;
import com.wedu.modules.dss.dao.DeviceProductionDao;
import com.wedu.modules.dss.dao.OperationStatusDao;
import com.wedu.modules.dss.entity.DeviceEntity;
import com.wedu.modules.dss.entity.DeviceProductionEntity;
import com.wedu.modules.dss.entity.OperationStatusEntity;
import com.wedu.modules.dss.entity.Vo.DeviceProductionEntityVo;
import com.wedu.modules.dss.service.DssDeviceProductionService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service("DssDeviceProductionService")
public class DeviceProductionImpl extends MPJBaseServiceImpl<DeviceProductionDao, DeviceProductionEntity> implements DssDeviceProductionService {
    @Autowired
    private DeviceProductionDao deviceProductionDao;
    @Autowired
    private DeviceDao deviceDao;
    @Autowired
    private OperationStatusDao operationStatusDao;

    @Override
    public PageUtils queryDeviceProduction(Map<String, Object> params) {
        String devicename = (String) params.get("name");
        String deviceId = (String) params.get("deviceId");
        MPJLambdaWrapper<DeviceProductionEntity> wrapper = new MPJLambdaWrapper<>();
        wrapper.select(DeviceEntity::getDeviceName, DeviceEntity::getDeviceId)
                .select(DeviceProductionEntity::getProduction, DeviceProductionEntity::getDate,DeviceProductionEntity::getDeviceId)
                .eq(DeviceEntity::getDeleted,0)
                .leftJoin(DeviceEntity.class, DeviceEntity::getId, DeviceProductionEntity::getDeviceId)
                .like(StringUtils.isNotBlank(devicename), DeviceEntity::getDeviceName, devicename)
                .like(StringUtils.isNotBlank(deviceId), DeviceEntity::getDeviceId, deviceId)
                .orderByDesc(DeviceProductionEntity::getDate);

        if (params.containsKey("startTime") && params.containsKey("endTime")) {
            long startTime = Long.parseLong(params.get("startTime").toString());
            long endTime = Long.parseLong(params.get("endTime").toString());
            wrapper.between(DeviceProductionEntity::getDate, new Date(startTime), new Date(endTime));
        }


        dailyTask();
        calculateUsageEfficiency();
        List<DeviceProductionEntityVo> deviceProductionEntityList = deviceProductionDao.selectJoinList(DeviceProductionEntityVo.class, wrapper);
        IPage<DeviceProductionEntity> page = this.page(new Query<DeviceProductionEntity>().getPage(params));
        int current = (int) page.getCurrent();
        int size = (int) page.getSize();
        int start = (current - 1) * size;
        int end = Math.min(start + size, deviceProductionEntityList.size());
        List<DeviceProductionEntityVo> paginatedList = deviceProductionEntityList.subList(start, end);
        IPage<DeviceProductionEntityVo> deviceProductionEntityVoIPage = new Page<>();
        deviceProductionEntityVoIPage.setTotal(deviceProductionEntityList.size());
        deviceProductionEntityVoIPage.setSize(size);
        deviceProductionEntityVoIPage.setPages(page.getPages());
        deviceProductionEntityVoIPage.setRecords(paginatedList);
        deviceProductionEntityVoIPage.setCurrent(current);
        return new PageUtils(deviceProductionEntityVoIPage);

    }

    /**
     * 计算设备每个使用日期的产量
     */
    //从dss_device_production表中查询所有设备的日期，并返回对应设备的日期和deviceId，从dss_operation_status根据deviceId。
    // 在dss_operation_status中根据id查询出在该日期的红灯(-1)时长，绿灯(1)时长，黄灯(0)时长，
    // 计算该日期的使用产量用该日期的全部绿灯时长相加/每个产品生成的所需要的时间5秒一个。
    //如果dss_operation_status的结束时间为空就用当前电脑时间进行当做结束时间并及时更新，如果出现好几天的等状态没有改变，则当天的灯时长，全天都是该灯时长，
    private void calculateUsageEfficiency() {
    LambdaQueryWrapper<DeviceProductionEntity> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(DeviceProductionEntity::getId,DeviceProductionEntity::getDeviceId, DeviceProductionEntity::getDate);
    List<DeviceProductionEntity> efficiencyEntities = deviceProductionDao.selectList(wrapper);
    for (DeviceProductionEntity efficiencyEntity : efficiencyEntities) {
        Date date = efficiencyEntity.getDate();
        Long deviceId = efficiencyEntity.getDeviceId();
        long[] lightDurations = calculateLightDurations(deviceId, date);
        long greenDuration = (lightDurations[1])/1000;
        int production = 0;
        if (greenDuration  > 0) {
            production = (int) (greenDuration / 20);
        }
        efficiencyEntity.setProduction(production);
        deviceProductionDao.updateById(efficiencyEntity);
    }
    }

    private long[] calculateLightDurations(Long deviceId, Date date) {
        long redDuration = 0;
        long greenDuration = 0;
        long yellowDuration = 0;
        // 获取指定日期的开始时间和结束时间
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND,0);
        cal.set(Calendar.MILLISECOND, 0);
        Date startOfDay = cal.getTime();
        // 获取指定日期的结束时间
        cal.add(Calendar.DAY_OF_YEAR, 1);
        Date endOfDay = cal.getTime();
        // 查询可能与指定日期有交集的操作状态记录
        LambdaQueryWrapper<OperationStatusEntity> statusWrapper = new LambdaQueryWrapper<>();
        statusWrapper.eq(OperationStatusEntity::getDeviceId, deviceId)
                .and(
                    w -> {
                    // 开始时间早于指定日期的结束时间
                    w.lt(OperationStatusEntity::getLightStartTime, endOfDay);
                    // 结束时间晚于指定日期的开始时间 或者 结束时间为 null
                    w.and(ww -> ww.gt(OperationStatusEntity::getLightEndTime, startOfDay)
                            .or().isNull(OperationStatusEntity::getLightEndTime));
                }
                );
        List<OperationStatusEntity> statusEntities = operationStatusDao.selectList(statusWrapper);
    for (OperationStatusEntity statusEntity : statusEntities){
        Date startTime = statusEntity.getLightStartTime();
        Date endTime = statusEntity.getLightEndTime();
        if (endTime == null) {
            endTime = new Date();
        }
        // 计算与指定日期的交集时长
        long start = Math.max(startTime.getTime(), startOfDay.getTime());
        long end = Math.min(endTime.getTime(), endOfDay.getTime());
        long duration = end - start;
        int status = statusEntity.getLightStatus();
        switch (status) {
            case -1:
                redDuration += duration;
                break;
            case 1:
                greenDuration += duration;
                break;
            case 0:
                yellowDuration += duration;
                break;
        }
    }
    return new long[]{redDuration, greenDuration, yellowDuration};
    }

    /**
     *Spring框架提供的一个定时任务方法，该方法会按照定义的cron表达式来执行。
     * cron 表达式的格式为：秒 分 时 日 月 星期几 年（可选）
     * @Scheduled(cron = "0 0 0 * * ?")表示每天0点执行一次。
     */
    @Scheduled(cron = "0 0 0 * * ?")
    private void dailyTask() {
     //获取所有设备的deviceId 和 startUseTime
        LambdaQueryWrapper<DeviceEntity> deviceWrapper = new LambdaQueryWrapper<>();
        deviceWrapper.select(DeviceEntity::getId, DeviceEntity::getStartUseTime);
        List<DeviceEntity> deviceEntities = deviceDao.selectList(deviceWrapper);
        // 获取当前日期，并将时间部分置为 0
        Calendar currentCal = Calendar.getInstance();
        currentCal.set(Calendar.HOUR_OF_DAY, 0);//小时数设置为0
        currentCal.set(Calendar.MINUTE, 0);//分钟数设置为0
        currentCal.set(Calendar.SECOND, 0);//秒数设置为0
        currentCal.set(Calendar.MILLISECOND, 0);//毫秒数设置为0
        Date currentDate = currentCal.getTime();//获取当前日期，并将时间部分置为 0
        for (DeviceEntity deviceEntity : deviceEntities) {
            Long deviceId = deviceEntity.getId();
            Date startUseTime = deviceEntity.getStartUseTime();
            if (startUseTime != null) {
                // 将 startUseTime 的时间部分置为 0
                Calendar startCal = Calendar.getInstance();
                startCal.setTime(startUseTime);
                startCal.set(Calendar.HOUR_OF_DAY, 0);
                startCal.set(Calendar.MINUTE, 0);
                startCal.set(Calendar.SECOND, 0);
                startCal.set(Calendar.MILLISECOND, 0);
                startUseTime = startCal.getTime();
           //检查该设备的最早在产量列表中使用记录
           LambdaQueryWrapper<DeviceProductionEntity> earliestWrapper = new LambdaQueryWrapper<>();
           earliestWrapper.eq(DeviceProductionEntity::getDeviceId, deviceId)
                   .orderByAsc(DeviceProductionEntity::getDate)
                   .last("LIMIT 1");
           DeviceProductionEntity earliestRecord = deviceProductionDao.selectOne(earliestWrapper);
           Date earliestDate = earliestRecord != null ? earliestRecord.getDate() : startUseTime;
           // 如果 startUseTime 比数据库中的最早日期还早，以 startUseTime 为准
           if (startUseTime.before(earliestDate)) {
               earliestDate = startUseTime;
           }
//         计算从最早日期到当前日期的天数差
                long daysBetween = calculateDaysBetween(earliestDate, currentDate);
           for (int i = 0; i <= daysBetween; i++){
               Calendar cal = Calendar.getInstance();
               cal.setTime(earliestDate);
               cal.add(Calendar.DAY_OF_YEAR, i);
               //检查当前日期的记录是否已经存在
               Date newDate = cal.getTime();
               if (!isRecordExists(deviceId, newDate)) {
                   DeviceProductionEntity deviceProductionEntity = new DeviceProductionEntity();
                   deviceProductionEntity.setDeviceId(deviceId);
                   deviceProductionEntity.setDate(new Timestamp(newDate.getTime()));
                   deviceProductionEntity.setProduction(0);
                   deviceProductionDao.insert(deviceProductionEntity);
               }
           }
            }
        }
    }

    private boolean isRecordExists(Long deviceId, Date newDate) {
        LambdaQueryWrapper<DeviceProductionEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeviceProductionEntity::getDeviceId, deviceId)
                .eq(DeviceProductionEntity::getDate, new Timestamp(newDate.getTime()));
        return deviceProductionDao.selectCount(wrapper) > 0;
    }

    private long calculateDaysBetween(Date earliestDate, Date currentDate) {
        long diffInMillis = currentDate.getTime() - earliestDate.getTime();
        return diffInMillis / (24 * 60 * 60 * 1000);
    }
}
